import { Asset, assetManager, sys } from 'cc';
import Boot from '../../Boot';
import { Loger } from "../loger/Loger";
import HttpUtils from '../net_work/HttpUtils';
import { HotUpdateEvent } from "./HotUpdateEvent";
import { HotUpdateInfo } from "./HotUpdateInfo";

export class HotOptions {
    OnVersionInfo: Function;
    OnNeedToUpdate: Function;
    OnNoNeedToUpdate: Function;
    OnUpdateFailed: Function;
    OnUpdateSucceed: Function;
    OnUpdateProgress: Function;

    check() {
        for (let key in this) {
            if (key !== 'check') {
                if (!this[key]) {
                    Loger.log(`参数HotOptions.${key}未设置！`);
                    return false;
                }
            }
        }
        return true
    }
}

export interface HotUpdateData {
    /**本地资源版本 */
    localHotVersion: string,
    /**远程资源版本 */
    remoteHotVersion: string,
    /**远程资源路径根 */
    baseUrl: string,
    /**远程资源manifest地址 */
    remoteManifestUrl: string,
    /**远程资源versionmanifest地址 */
    remoteVersionUrl: string,
    /**本地资源manifest地址 */
    localManifestUrl: string,
    /**本地资源下载路径 */
    localPackageUrl: string,
    /**远程资源下载路径 */
    remotePackageUrl: string,

}

class Hot {
    private data: HotUpdateData = {
        localHotVersion: "",
        remoteHotVersion: "",
        baseUrl: "",
        localManifestUrl: "",
        remoteManifestUrl: "",
        remoteVersionUrl: "",
        localPackageUrl: "",
        remotePackageUrl:"",
    }

    _assetsMgr: jsb.AssetsManager = null;
    _options: HotOptions = null;
    _state = Hot.State.None;
    storagePath: string = "";
    static State = {
        None: 0,
        Check: 1,
        Update: 2,
    }

    hotUpdate() {
        if (!this._assetsMgr) {
            Loger.log('请先初始化')
            return
        }
        this._assetsMgr.setEventCallback(this._hotUpdateCallBack.bind(this));
        this._state = Hot.State.Update;
        this._assetsMgr.update();
    }

    _hotUpdateCallBack(event: jsb.EventAssetsManager) {
        let code = event.getEventCode();
        Loger.log(`hotUpdate Code: ${code}`);
        switch (code) {
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                Loger.log("已经和远程版本一致，无须更新");
                this._options.OnNoNeedToUpdate && this._options.OnNoNeedToUpdate(code)
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                Loger.log('发现新版本,请更新');
                this._options.OnNeedToUpdate && this._options.OnNeedToUpdate(code);
                break;
            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                Loger.log('更新中...')
                if (this._state === Hot.State.Update) {
                    this._options.OnUpdateProgress && this._options.OnUpdateProgress(event);
                } else {
                    // 检查状态下，不回调更新进度
                }
                break;
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                Loger.log('更新成功');
                this._onUpdateFinished();
                break;
            case jsb.EventAssetsManager.ASSET_UPDATED:
                // 不予理会的消息事件
                break;
            default:
                this._onUpdateFailed(code);
                break;
        }
    }

    _onUpdateFailed(code) {
        this._assetsMgr.setEventCallback(null)
        this._options.OnUpdateFailed && this._options.OnUpdateFailed(code);
    }

    // 更新完成
    _onUpdateFinished() {
        this._assetsMgr.setEventCallback(null)
        let searchPaths = jsb.fileUtils.getSearchPaths();
        let newPaths = this._assetsMgr.getLocalManifest().getSearchPaths();
        Loger.log("[HotUpdate] 搜索路径: " + JSON.stringify(newPaths));
        Array.prototype.unshift(searchPaths, newPaths);
        sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));

        jsb.fileUtils.setSearchPaths(searchPaths);
        this._options.OnUpdateSucceed && this._options.OnUpdateSucceed();
    }

    showSearchPath() {
        Loger.log("========================搜索路径========================");
        let searchPaths = jsb.fileUtils.getSearchPaths();
        for (let i = 0; i < searchPaths.length; i++) {
            Loger.log("[" + i + "]: " + searchPaths[i]);
        }
        Loger.log("======================================================");
    }

    /**替换目标下载地址 */
    replaceUrl(nativeAsset: string): string {
        // "version": "1.16.0", 
        // "packageUrl": "http://wjgy.yunhoon.com/lucky/game_res_debug/ver_top", 
        // "remoteManifestUrl": "http://wjgy.yunhoon.com/lucky/game_res_debug/ver_top/project.manifest", 
        // "remoteVersionUrl": "http://wjgy.yunhoon.com/lucky/game_res_debug/ver_top/version.manifest",
        Loger.log("====localAssets._nativeAsset", nativeAsset);
        Loger.log("====localAssets._nativeAsset json", JSON.parse(nativeAsset));
        let assetJson = JSON.parse(nativeAsset);
        assetJson["version"] = this.data.localHotVersion;
        assetJson["packageUrl"] = this.data.remotePackageUrl;
        assetJson["remoteManifestUrl"] = this.data.remoteManifestUrl; // `${this.data.packageUrl}/${this.getHotVersionFolderName(this.data.remoteHotVersion)}/project.manifest`;
        assetJson["remoteVersionUrl"] = this.data.remoteVersionUrl; // `${this.data.packageUrl}/${this.getHotVersionFolderName(this.data.remoteHotVersion)}/version.manifest`;
        Loger.log("===替换后的asset", assetJson);
        Loger.log("===替换后的localAssets._nativeAsset === ", JSON.stringify(assetJson));
        return JSON.stringify(assetJson)
    }

    testAsset() {
        assetManager.loadRemote(this.data.localManifestUrl, (err, localAssets: Asset & { _nativeAsset: string }) => {
            Loger.log("localAssets._nativeAsset", localAssets._nativeAsset);
            Loger.log("localAssets._nativeAsset json", JSON.parse(localAssets._nativeAsset));
            let asset = JSON.parse(localAssets._nativeAsset);

            // "version": "1.16.0", 
            // "packageUrl": "http://wjgy.yunhoon.com/lucky/game_res_debug/ver_top", 
            // "remoteManifestUrl": "http://wjgy.yunhoon.com/lucky/game_res_debug/ver_top/project.manifest", 
            // "remoteVersionUrl": "http://wjgy.yunhoon.com/lucky/game_res_debug/ver_top/version.manifest",
            asset["packageUrl"] = this.data.localPackageUrl;
            asset["remoteManifestUrl"] = this.data.remoteManifestUrl;
            asset["remoteVersionUrl"] = this.data.remoteVersionUrl;

            Loger.log("==替换后的asset", asset);

            Loger.log("===替换后的localAssets._nativeAsset === ", JSON.stringify(asset));

        })
    }
    /**
        * 大版本更新检测
        * 审核情况不开热更
        * 大版本更新完还得热更
        * manifest地址服务器传
        * 是不是测试服
        */
    checkVersion(options: HotOptions, data: HotUpdateInfo) {
        // GameHttpUtils.getWithCallback(`${Boot.serverUrl}/app/hot?v=${Boot.versionName}&channel=${Boot.channel}`, null, (data: HotUpdateInfo) => {
        //     if (data) {
        //         Loger.log("===服务器返回的版本检查数据：", JSON.stringify(data));
        //         if (data.force) {
        //             //大版本更新
        //             // cc.sys.openURL(data.DownUrl);
        //             let verInfo: VersionInfo = {
        //                 desc: data.desc || "版本更新",
        //                 downloadUrl: data.DownUrl,
        //                 force: 1
        //             };
        //             ViewMgr.getInstance().showView(new VMVersionCheck(verInfo, null));
        //         } else {
        //热更
        Loger.log("=====热更checkVersion " + JSON.stringify(data));
        this.data.localHotVersion = Boot.hotVersion;
        this.data.remoteHotVersion = data.hotVersion;
        const remotePath = this.getHotVersionFolderName(this.data.remoteHotVersion);
        const localPath = this.getHotVersionFolderName(this.data.localHotVersion)
        this.data.baseUrl = data.hotUrl;
        this.data.localPackageUrl = `${this.data.baseUrl}/${localPath}`;
        this.data.remotePackageUrl = `${this.data.baseUrl}/${remotePath}`;
        this.data.remoteManifestUrl = `${this.data.baseUrl}/${remotePath}/project.manifest`;
        this.data.remoteVersionUrl = `${this.data.baseUrl}/${remotePath}/version.manifest`;
        this.data.localManifestUrl = `${this.data.baseUrl}/${localPath}/project.manifest`;
        // Loger.log("===热更版本号localVersion,remoteVersion", Boot.hotVersion, this.data.remoteHotVersion);
        let result: number = this.versionCompareHandle(this.data.localHotVersion, this.data.remoteHotVersion);
        Loger.log("===热更版本号比较结果:", result);
        if (result < 0) {
            //需要热更
            this.init(options);
        } else {
            Loger.log("===不需要热更");
            HotUpdateEvent.ON_HOT_UPDATE_DONE.emit();
        }
        //     }
        // } else {
        //     Loger.log("===不开启热更");
        //     GameLaunchEvent.ON_HOT_UPDATE_DONE.emit();
        // }
        // })
    }
    /**根据资源版本号转换资源地址 */
    private getHotVersionFolderName(hotVersion: string) {
        const arr = hotVersion.split('.')
        return `ver_${arr[0]}_${arr[1]}`
    }
    versionCompareHandle(versionA, versionB) {
        // 比较版本
        Loger.log("====客户端版本: " + versionA + ', 当前最新版本: ' + versionB);
        let vA = versionA.split('.');
        let vB = versionB.split('.');
        for (let i = 0; i < vA.length; ++i) {
            let a = parseInt(vA[i]);
            let b = parseInt(vB[i] || '0');
            if (a === b) {
                continue;
            } else {
                return -1;
            }
        }
        if (vB.length > vA.length) {
            return -1;
        } else {
            return 0;
        }
    }
    // ------------------------------初始化------------------------------
    init(opt: HotOptions) {
        if (!sys.isNative) {
            return;
        }
        if (!opt.check()) {
            return;
        }
        this._options = opt;

        if (this._assetsMgr) {
            return;
        }

        this.showSearchPath();
        this.storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'remote-asset');

        Loger.log("====远程manifest url=", this.data.remoteManifestUrl);
        Loger.log("====远程local manifest url=", this.data.localManifestUrl);
        Loger.log("====热更新存储路径=", this.storagePath);

        this._assetsMgr = new jsb.AssetsManager("", this.storagePath, (versionA, versionB) => {
            // 比较版本
            // Loger.log("====客户端版本: " + versionA + ', 当前最新版本: ' + versionB);
            this._options.OnVersionInfo({ local: versionA, server: versionB });
            return this.versionCompareHandle(versionA, versionB);
        });
        Loger.log("====准备下载远程本地资源====", this.data.localManifestUrl)
        // cc.assetManager.loadRemote(this.data.localManifestUrl, (err, localAssets: cc.Asset & { _nativeAsset: string }) => {
            // Loger.log("=====加载远程资源loadLocal err=", err);
        HttpUtils.get(this.data.localManifestUrl, null, (localAssets) => {
            Loger.log("=====加载远程资源loadLocal localAssets=", localAssets);
            if (localAssets == "404") {
                //远程资源不存在,跳过热更
                Loger.log("=====远程资源不存在,跳过热更===", localAssets);
                opt.OnNoNeedToUpdate();
                return;
            }
            Loger.log("localAssets._nativeAsset === ", JSON.stringify(localAssets._nativeAsset));

            this.deleteLocalManifest();
            // let asset = this.replaceUrl(localAssets._nativeAsset);
            let asset = this.replaceUrl(JSON.stringify(localAssets));
            var manifest = new jsb.Manifest(asset, this.storagePath);
            this._assetsMgr.loadLocalManifest(manifest, this.storagePath);

            this._assetsMgr.setVerifyCallback((assetsFullPath, asset) => {
                let { compressed, md5, path, size } = asset;
                Loger.log("===setVerifyCallback=compressed===", path)
                Loger.log("===setVerifyCallback=path===", path)
                if (compressed) {
                    return true;
                } else {
                    return true;
                }
            })
            if (sys.os === sys.OS.ANDROID) {
                // 安卓手机设置 最大并发任务数量限制为2
                // this._assetsMgr.setMaxConcurrentTask(10);
            }

            let localManifest = this._assetsMgr.getLocalManifest()
            Loger.log('===[HotUpdate] 热更新资源存放路径: ' + this.storagePath);
            Loger.log('===[HotUpdate] 远程manifest路径: ' + this.data.remoteManifestUrl);
            Loger.log('===[HotUpdate] local packageUrl: ' + localManifest.getPackageUrl());
            Loger.log('===[HotUpdate] project.manifest remote url: ' + localManifest.getManifestFileUrl());
            Loger.log('===[HotUpdate] version.manifest remote url: ' + localManifest.getVersionFileUrl());
            this.checkUpdate();
        })
    }

    /**删除本地缓存的配置文件 */
    deleteLocalManifest(){
        let localProjectManifestPath = `${this.storagePath}/project.manifest`;
        let localVersionManifestPath = `${this.storagePath}/version.manifest`;
        Loger.log("=========准备删除本地manifest文件===============", this.storagePath);
        if (jsb.fileUtils.isFileExist(localProjectManifestPath)) {
            if(jsb.fileUtils.removeFile(localProjectManifestPath)){
                Loger.log("===删除本地project.manifest成功===", localProjectManifestPath)
            } else {
                Loger.log("===删除本地project.manifest失败===", localProjectManifestPath)
            }
        }else{
            Loger.log("====本地project.manifest不存在,不用删除")
        }
        if (jsb.fileUtils.isFileExist(localVersionManifestPath)) {
            if(jsb.fileUtils.removeFile(localVersionManifestPath)){
                Loger.log("===删除本地version.manifest成功===", localVersionManifestPath)
            }else{
                Loger.log("===删除本地version.manifest失败===", localVersionManifestPath)
            }
        } else {
            Loger.log("====本地version.manifest不存在,不用删除")
        }
    }

    // 检查更新
    checkUpdate() {
        if (!this._assetsMgr) {
            Loger.log('请先初始化')
            return;
        }
        Loger.log("=======checkUpdate======", this._assetsMgr.getState(), jsb.AssetsManager.State.UNINITED)
        if (this._assetsMgr.getState() == jsb.AssetsManager.State.UNINITED) {
            Loger.error('未初始化')
            return;
        }

        if (!this._assetsMgr.getLocalManifest() || !this._assetsMgr.getLocalManifest().isLoaded()) {
            Loger.log("================Failed to load local manifest ...");
            return;
        }


        this._assetsMgr.setEventCallback(this._hotUpdateCallBack.bind(this));
        this._state = Hot.State.Check;
        // 下载version.manifest，进行版本比对
        this._assetsMgr.checkUpdate();
    }

}


let hotInstance = new Hot();

export default hotInstance;
